How to easily access a web.config AppSettings value with a Type and a default value?

Update: This is now in my Rhyous.Collections NuGet package and you can see the source on GitHub: NameValueCollectionExtensions.cs

I wanted to make it easier to get a value from AppSettings in the web.config (or the app.config if you aren’t doing web) while converting to the proper type and having a default value.

Here is the syntax I started with that I didn’t like at all.

public static int MaxRetryAttempts = (string.IsNullOrWhiteSpace(ConfigurationManager.AppSettings["SmptRetries"]))
    ? int.Parse(ConfigurationManager.AppSettings["SmptRetries"])
    : 3;

I decided I wanted to have the following syntax:

ConfigurationManager.AppSettings.Get<T>(string key, T defaultValue)

I found a blog post that got me started. His syntax was very close to what I wanted already. He didn’t have the default value and he wasn’t an extension method, but wow, was this a real help. I was unaware of TypeDescriptor.GetConverter and was going to basically roll my own with an massively ugly case statement. So I am very happy I found his post.

I created the following NameValueCollectionExtensions.cs file.

using System.Collections.Specialized;
using System.ComponentModel;

namespace Rhyous.Extensions
{
    public static class NameValueCollectionExtensions
    {
        public static T Get<T>(this NameValueCollection collection, string key, T defaultValue)
        {
            var value = collection[key];
            var converter = TypeDescriptor.GetConverter(typeof(T));
            if (string.IsNullOrWhiteSpace(value) || !converter.IsValid(value))
            {
                return defaultValue;
            }

            return (T)(converter.ConvertFromInvariantString(value));
        }
    }
}

Details

  1. ConfigurationManager.AppSettings is of Type System.Collections.Specialized.NameValueCollection. So my extension method must be for that type.
  2. I changed the author’s method to be an extension method using the “this” keyword.
  3. I changed from using ConfirationManager.AppSettings to use the first parameter, collection, defined by the “this” keyword.
  4. I added a parameter for a default value.
  5. I changed the method code to return the default value, instead of throwing an exception, if the setting in the web.config is missing or empty.

Usage

I have to retry sending emails. I want the SmtpRetries to be an int obtained from the web.config’s AppSettings and have a default value of 3.

public static int MaxRetryAttempts = ConfigurationManager.AppSettings.Get("SmptRetries", 3);

Unit Tests

I really only wrote unit tests for int conversion. I expect that is sufficient. But if you want to write tests for a double or a other type feel free.

using System;
using System.Collections.Specialized;
using Microsoft.VisualStudio.TestTools.UnitTesting;

namespace Rhyous.Extensions.Tests
{
    [TestClass]
    public class NameValueCollectionExtensionsTests
    {
        private const string Name = "Retries";

        [TestMethod]
        public void IntValueExistsTest()
        {
            // Arrange
            const int defaultMaxRetries = 2;
            const int value = 3;
            var collection = new NameValueCollection { { Name, value.ToString() } };

            // Act
            var actual = collection.Get(Name, defaultMaxRetries);

            // Assert
            Assert.AreEqual(value, actual, "Valid value should return the valid value.");
        }

        [TestMethod]
        public void IntValueDoesNotExistTest()
        {
            // Arrange
            const int defaultMaxRetries = 2;
            var collection = new NameValueCollection();

            // Act
            var actual = collection.Get(Name, defaultMaxRetries);

            // Assert
            Assert.AreEqual(defaultMaxRetries, actual, "Missing value returns default.");
        }

        [TestMethod]
        public void IntValueIsEmptyStringTest()
        {
            // Arrange
            const int defaultMaxRetries = 2;
            var collection = new NameValueCollection { { Name, string.Empty } };

            // Act
            var actual = collection.Get(Name, defaultMaxRetries);

            // Assert
            Assert.AreEqual(defaultMaxRetries, actual, "Empty string returns default.");
        }

        [TestMethod]
        public void IntValueIsWhiteSpaceStringTest()
        {
            // Arrange
            const int defaultMaxRetries = 2;
            var collection = new NameValueCollection { { Name, "  " } };

            // Act
            var actual = collection.Get(Name, defaultMaxRetries);

            // Assert
            Assert.AreEqual(defaultMaxRetries, actual, "Whitespace string returns default.");
        }

        [TestMethod]
        public void IntValueIsDoubleTest()
        {
            // Arrange
            const int defaultMaxRetries = 2;
            const double value = 3.5; 
            var collection = new NameValueCollection { { Name, value.ToString() } };

            // Act
            var actual = collection.Get(Name, defaultMaxRetries);

            // Assert
            Assert.AreEqual(defaultMaxRetries, actual, "Invalid value returns default.");
        }

        [TestMethod]
        public void IntValueIsCharsTest()
        {
            // Arrange
            const int defaultMaxRetries = 2;
            const string value = "abc";
            var collection = new NameValueCollection { { Name, value } };

            // Act
            var actual = collection.Get(Name, defaultMaxRetries);

            // Assert
            Assert.AreEqual(defaultMaxRetries, actual, "Invalid value returns default.");
        }
    }
}

5 Comments

  1. Mike says:

    Thanks for creating NameValueCollectionExtensions. I found your link on StackOverflow but having only just joined SO I don't have sufficient "reputation" to UpVote it. However your code works a treat!

  2. Jim says:

    Ahh - so site is stripping out the < T > symbols

  3. Jim says:

    Nice! - small typo in the code for the extension however:

    public static T Get(...

    should be:

    public static T Get<T>(...

    Jim

Leave a Reply

How to post code in comments?